﻿using Newtonsoft.Json;
using System;
using System.IO;
using System.Runtime.CompilerServices;

namespace WMS.Core
{

    /// <summary>
    /// 日志操作类(单例)
    /// </summary>
    public class Logs
    {
        private Logs()
        {

        }

        private static readonly object writeToFileMutex = new object();

        public static Logs Instance { get; private set; } = new Logs();

        /// <summary>
        /// 当前日志文件路径
        /// </summary>
        private string logFilename = string.Empty;

        /// <summary>
        /// 最后一次创建文件时间
        /// </summary>
        private DateTime? LastCreateTime = null;

        /// <summary>
        /// 当前日志文件, 已写入尺寸.
        /// </summary>
        private int writedSize = 0;

        /// <summary>
        /// 单个日志文件最大尺寸(20MB)
        /// </summary>
        private const int MaxLogFileSize = 20 * 1024 * 1024; //20MB

        /// <summary>
        /// 是否写入到日志文件
        /// </summary>
        public bool UseLogFile { get; set; } = true;

        /// <summary>
        /// 输出异常日志
        /// </summary>
        /// <param name="ex"></param>
        public void Out<TEx>(TEx ex, string prefix = "")
            where TEx : Exception
        {
            string prefixStr = prefix.Length > 0
                ? $"{prefix}, "
                : string.Empty
            ;
            string info = string.Join(
                Environment.NewLine,
                $"{prefixStr}发生错误:",
                $"   错误描述:[{ex.Message}].",
                $"   详情:[{ex.ToString()}]."
            );
            Out(info);
        }

        /// <summary>
        /// 输出异常日志
        /// </summary>
        /// <param name="ex"></param>
        public void Out<TEx>(TEx ex, string prefix, params string[] attchs)
            where TEx : Exception
        {
            string prefixStr = prefix.Length > 0
                ? $"{prefix}, "
                : string.Empty
            ;

            string attchStr = attchs.Length > 0 ? $"   附加数据: [{string.Join(Environment.NewLine, attchs)}]." : "";
            string info = string.Join(
                Environment.NewLine,
                $"{prefixStr}发生错误:",
                $"   错误描述:[{ex.Message}].",
                $"   详情:[{ex.ToString()}].",
                attchStr
            );
            Out(info);
        }

        /// <summary>
        /// SQL语句执行错误输出
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="obj"></param>
        /// <param name="ex"></param>
        public void OutSqlEx(string sql, object obj, Exception ex)
        {
            var msg = $"执行Sql时出错, sql=[{sql}], paramster=[{JsonConvert.SerializeObject(obj)}]{Environment.NewLine}";
            Out(ex, msg);
        }

        /// <summary>
        /// SQL语句执行错误输出
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="ex"></param>
        public void OutSqlEx(string sql, Exception ex)
        {
            var msg = $"执行Sql时出错, sql=[{sql}], {Environment.NewLine}";
            Out(ex, msg);
        }

        /// <summary>
        /// 输出常规日志
        /// </summary>
        /// <param name="msg"></param>
        public void Out(string splitString, params string[] msgs)
        {
            DateTime.Now.ToString();
            var msg = msgs.Length > 0 ? string.Join(splitString, msgs) : splitString;
            string line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}\t{msg}{Environment.NewLine}";
            WriteToFile(line);
            WriteToConsole(line);
        }

        /// <summary>
        /// 写入到Console控制台
        /// </summary>
        /// <param name="msg"></param>
        private void WriteToConsole(string msg)
        {
            Console.Write(msg);
        }

        /// <summary>
        /// 获取日志文件名
        /// </summary>
        /// <returns></returns>
        public string GetLogFileName()
        {
            //创建时间, 已切换一日
            var tm = (LastCreateTime ?? DateTime.MinValue).Date.AddDays(1);
            if (tm < DateTime.Now)
            {
                logFilename = CreateNewFilename();
            }

            //超过最大写入容量
            if (writedSize > MaxLogFileSize)
            {
                logFilename = CreateNewFilename();
            }

            return logFilename;
        }

        /// <summary>
        /// 创建新日志文件名
        /// </summary>
        /// <returns></returns>
        private string CreateNewFilename()
        {
            DateTime now = DateTime.Now;
            string ret = Path.Combine(
                AppDomain.CurrentDomain.BaseDirectory,
                "Logs",
                $"{now:yyyy}",
                $"{now:yyyyMM}",
                $"{now:yyyyMMdd}",
                $"log_{now:yyyyMMdd_HHmmss}.log"
            );
            writedSize = 0;
            LastCreateTime = DateTime.Now;
            return ret;
        }

        /// <summary>
        /// 写入到日志文件
        /// </summary>
        /// <param name="line"></param>
        public void WriteToFile(string line)
        {
            if (!UseLogFile)
            {
                return;
            }

            lock (writeToFileMutex)
            {
                string file = GetLogFileName();
                string _ = Utils.GetOrCreateDirectory(file);
                File.AppendAllText(file, line);
                writedSize += line.Length;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="prefix"></param>
        public void OutJson<T>(
            T obj,
            string objName = "",
            [CallerMemberName] string callerMember = "<Unknow>"
        )
        {
            var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
            var typeName = typeof(T).FullName;
            if (string.IsNullOrEmpty(objName))
            {
                objName = "obj";
            }
            var msg =
                $"{callerMember}=>[{objName}:{typeName}]={json}" +
                $"{Environment.NewLine}"
            ;
            Out(msg);
        }
    }

}
